<?php namespace sacvoyage\storage;

require_once("sacvoyage/Pathfinder.php"); 
use \sacvoyage\Pathfinder;

require_once("QueryResultIterator.php");

require_once("sacvoyage/ArrayToObjectIterator.php");
use \sacvoyage\ArrayToObjectIterator;

/*
 * 
 *
 */
class Sorter implements \Iterator {

	public function __construct(&$table, $keyIterator=null, $order=null) {
		$this->_table=$table;
		$this->_keyIterator=$keyIterator;
		$this->_order=$order;
	}

	public function rewind() {
		if(!$this->_resultIterator) {
			$this->_resultIterator=$this->_getSortedKeysIterator(
				$this->_table, 
				$this->_keyIterator, 
				$this->_order
			);
		}
		$this->_resultIterator->rewind();
	}

	public function key() {
		return $this->_resultIterator->key();
	}

	public function current() {
		return $this->_resultIterator->current();
	}
	
	public function next() {
		$this->_resultIterator->next();
	}

	public function valid() {
		return $this->_resultIterator->valid();
	}

	private $_table;
	private $_keyIterator;
	private $_orders;
	private $_resultIterator;

	private function _getSortedKeysIterator($table, $keyIterator, $order) {

		$orders=$this->_getOrders($order);
		
		if($orders) { // if at least one sorting order is set
			
			// if keys are supplied, create temp table and fill it with keys
			if($keyIterator) {
				$baseTableExpression='temp_table_'.random_string();
				$baseTableAlias=$baseTableExpression;
				$table->query(
					"CREATE TEMPORARY TABLE $baseTableExpression ".
					"(_id TEXT, _rev TEXT)"
				);

				$insertValues=array();
				foreach($keyIterator as $key) {
					$_id=$table->escape($key->_id);
					$_rev=$table->escape($key->_rev);
					$insertValues[]="('$_id', '$_rev')";
				}
				if($insertValues) {
					$table->query(
						"INSERT INTO $baseTableExpression (_id, _rev) ".
						"VALUES ".implode(', ', $insertValues)
					);
				}

			} else {
				$baseTableExpression=$table->getExpression();
				$baseTableAlias=$table->getAlias();
			}

			$joins=array();
			$orderbys=array();
			foreach($orders as $order) {
				$alias='alias_'.random_string();
				$path=$table->escape($order['path']);
	
				$joins[]="{$table->getExpression()} AS {$alias} ".
						 "ON {$baseTableAlias}._id={$alias}._id ".
						 "AND {$baseTableAlias}._rev={$alias}._rev ".
						 "AND {$alias}.path='{$path}'";
	
				$orderbys[]="{$alias}.value {$order['order']}";
			}

			$sql="SELECT DISTINCT {$baseTableAlias}._id, ".
			     "{$baseTableAlias}._rev ".
				 "FROM {$baseTableExpression} AS $baseTableAlias LEFT JOIN ".
				 implode(' LEFT JOIN ', $joins)." ".
				 "ORDER BY ".implode(', ', $orderbys);
			
			$result=$table->query($sql);
			
			if($keyIterator) {	
				$table->query("DROP TABLE {$baseTableAlias}");
			}

			return new ArrayToObjectIterator(
				new QueryResultIterator($result)
			);

		} else { // if no sort orders supplied
			if($keyIterator) { // have keys but nothing to do
				return $keyIterator;
			} else {
				$sql="SELECT DISTINCT {$table->getAlias()}._id, ".
				     "{$table->getAlias()}._rev ".
					 "FROM {$table->getExpression()} AS {$table->getAlias()}";
				$result=$table->query($sql);
				return new ArrayToObjectIterator(
					new QueryResultIterator($result)
				);
			}
		}


	}
	
	private function _getOrders($orders) {
		if($orders===null) {
			return null;
		} else {

			if(!is_enumerative_array($orders)) {
				throw new Exception('orders must be enumerative array');
			}
			$pathOrders=array();
			foreach($orders as $order) {
				$finder=new Pathfinder($order);
				foreach($finder as $path=>$value) {
					$pathOrders[]=array(
						'path'=> $path, 
						'order'=> $value!='desc' ? 'asc' : 'desc'
					);
					break;
				}
			}
		
			return $pathOrders;	
		}
	}

}


?>